
#region Imported Namespaces
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;
#endregion

namespace PHSRAG.WebControls
{
	#region Class Pager
	/// <summary>
	/// The Pager assists in navigation of data within controls such as DataGrid. It contains the following:
	/// Range of record numbers being displayed, current page number, total page count, and links to navigate to the
	/// first, previous, next, last pages.
	/// The Pager does not do much other than to display its state. Its consumers should wire up event handlers to the
	/// appropriate set of events and manage the actual navigation within the relevant data bound control.
	/// </summary>
	/// <remarks>
	/// Pages which have a DataGrid in them, and wish NOT to use the built-in DataGrid pagination control should use
	/// the Page control. Note that more than one Page control may be used and associated with one DataGrid (for exmple,
	/// one on top and another on the bottom of the DataGrid).
	/// </remarks>
	/// <example>
	/// <p>The following example shows how the page would use this control. The registration of the control can be done
	/// simply by dropping the control on the page in design mode.</p>
	/// <p>Note that the <b>FirstRecord</b>, <b>LastRecord</b>, <b>RecordCount</b>, and <b>CurrentPage</b>
	/// properties are typically set at runtime. The example shows them being set statically for illustration only.</p>
	/// <code>
	/// <![CDATA[
	/// <%@ Register TagPrefix="cc1" Namespace="PHSRAG.WebControls" Assembly="PHSRAG.WebControls" %>
	/// ...
	/// <body>
	/// ...
	/// <cc1:Pager id="pager" runat="server" LastRecord="5" PageCount="20" FirstRecord="1" RecordCount="100" CurrentPage="1" />
	/// ...
	/// </body>
	/// ]]>
	/// </code>
	/// </example>
	[ToolboxData("<{0}:Pager runat=server />"), Designer(typeof(PagerDesigner))]
	public class Pager : System.Web.UI.WebControls.WebControl, INamingContainer
	{
		#region Constants
		private const string FirstPageCommand = "First Page";
		private const string LastPageCommand = "Last Page";
		private const string NextPageCommand = "Next Page";
		private const string PreviousPageCommand = "Previous Page";
		private const string RecordLabelKey = "RecordLabel_B3D6BD65-EA8D-48f3-8F6D-123371FF0339";
		private const string UseOptionalLeftTextKey = "UseOptionalLeftText_B3D6BD65-EA8D-48f3-8F6D-123371FF0339";
		private const string UseOptionalRightTextKey = "UseOptionalRightText_B3D6BD65-EA8D-48f3-8F6D-123371FF0339";
		
	#endregion

		#region Instance Variables
		private Label		currentPage;
		private LinkButton	firstPage;
		private Label		firstRecord;
		private LinkButton	lastPage;
		private Label		lastRecord;
		private LinkButton	nextPage;
		private Label		pageCount;
		private LinkButton	previousPage;
		private Label		recordCount;
		private Label		recordLabel;
		private Label		leftText;
		private Label		rightText;
		#endregion

		#region Properties
		/// <summary>
		/// Get/Set the current page number.
		/// </summary>
		[Bindable(true), Category("Pagination"), Description("Get/Set the current page number")] 
		public int CurrentPage
		{
			get { EnsureChildControls(); return GetIntegerSafely(currentPage); }
			set 
            { 
                EnsureChildControls(); 
                currentPage.Text = value.ToString();
                EnableFirstPage = EnablePreviousPage = (value != 1);
                EnableLastPage = EnableNextPage = (value != PageCount);
            }
		}
		
		/// <summary>
		/// Get/Set the first record number on the current page.
		/// </summary>
		[Bindable(true), Category("Pagination"), Description("Get/Set the first record number")] 
		public int FirstRecord
		{
			get { EnsureChildControls(); return GetIntegerSafely(firstRecord); }
			set { EnsureChildControls(); firstRecord.Text = value.ToString(); }
		}
		
		/// <summary>
		/// Get/Set the last record number on the current page.
		/// </summary>
		[Bindable(true), Category("Pagination"), Description("Get/Set the last record number")] 
		public int LastRecord
		{
			get { EnsureChildControls(); return GetIntegerSafely(lastRecord); }
			set { EnsureChildControls(); lastRecord.Text = value.ToString(); }
		}
		
		/// <summary>
		/// Get/Set the total page count.
		/// </summary>
		[Bindable(true), Category("Pagination"), Description("Get/Set the total page count")] 
		public int PageCount
		{
			get { EnsureChildControls(); return GetIntegerSafely(pageCount); }
			set { EnsureChildControls(); pageCount.Text = value.ToString(); }
		}
		
		/// <summary>
		/// Get/Set the total record count.
		/// </summary>
		[Bindable(true), Category("Pagination"), Description("Get/Set the total record count")] 
		public int RecordCount
		{
			get { EnsureChildControls(); return GetIntegerSafely(recordCount); }
			set { EnsureChildControls(); recordCount.Text = value.ToString(); }
		}

		/// <summary>
		/// Get/Set the record label.
		/// </summary>
		[Bindable(true), Category("Display"), Description("Get/Set the record label")] 
		public string RecordLabel
		{
			get { object o = ViewState[RecordLabelKey]; return (o == null) ? "Record" : (string)o; }
			set { ViewState[RecordLabelKey] = value; }
		}

		/// <summary>
		/// Enable/Disable the first-page link.
		/// </summary>
		[Bindable(true), Category("Behavior"), Description("Enable/Disable the first-page link")] 
		public bool EnableFirstPage
		{
			get { EnsureChildControls(); return firstPage.Enabled; }
			set { EnsureChildControls(); firstPage.Enabled = value; }
		}

		/// <summary>
		/// Enable/Disable the last-page link.
		/// </summary>
		[Bindable(true), Category("Behavior"), Description("Enable/Disable the last-page link")] 
		public bool EnableLastPage
		{
			get { EnsureChildControls(); return lastPage.Enabled; }
			set { EnsureChildControls(); lastPage.Enabled = value; }
		}

		/// <summary>
		/// Enable/Disable the next-page link.
		/// </summary>
		[Bindable(true), Category("Behavior"), Description("Enable/Disable the next-page link")] 
		public bool EnableNextPage
		{
			get { EnsureChildControls(); return nextPage.Enabled; }
			set { EnsureChildControls(); nextPage.Enabled = value; }
		}

		/// <summary>
		/// Enable/Disable the previous-page link.
		/// </summary>
		[Bindable(true), Category("Behavior"), Description("Enable/Disable the previous-page link")] 
		public bool EnablePreviousPage
		{
			get { EnsureChildControls(); return previousPage.Enabled; }
			set { EnsureChildControls(); previousPage.Enabled = value; }
		}

		/// <summary>
		/// Get/Set the left text label 
		/// </summary>
		[Bindable(true), Category("Display"), Description("Get/Set the left text label")] 
		public string LeftText
		{
			get { EnsureChildControls(); return leftText.Text; }
			set { EnsureChildControls(); leftText.Text = value.ToString(); }
		}

		/// <summary>
		/// Get/Set the right text label 
		/// </summary>
		[Bindable(true), Category("Display"), Description("Get/Set the right text label")] 
		public string RightText
		{
			get { EnsureChildControls(); return rightText.Text; }
			set { EnsureChildControls(); rightText.Text = value.ToString(); }
		}

		[Bindable(true), Category("Behavior"), Description("Enable/Disable utilization of user defined left text")] 
		public bool UseOptionalLeftText
		{
			get { object o = ViewState[UseOptionalLeftTextKey]; return (o == null) ? false : (bool)o; }
			set { ViewState[UseOptionalLeftTextKey] = value; }
		}

		[Bindable(true), Category("Behavior"), Description("Enable/Disable utilization of user defined right text")] 
		public bool UseOptionalRightText
		{
			get { object o = ViewState[UseOptionalRightTextKey]; return (o == null) ? false : (bool)o; }
			set { ViewState[UseOptionalRightTextKey] = value; }
		}
		#endregion

		#region Events
		/// <summary>
		/// Add/Remove handler for the first-page click.
		/// </summary>
		[Category("Action"), Description("Raised when the first-page is clicked")]
		public event CommandEventHandler FirstPage
		{
			add		{ Events.AddHandler(FirstPageCommand, value); }
			remove	{ Events.RemoveHandler(FirstPageCommand, value); }
		}
		
		/// <summary>
		/// Add/Remove handler for the last-page click.
		/// </summary>
		[Category("Action"), Description("Raised when the last-page is clicked")]
		public event CommandEventHandler LastPage
		{
			add		{ Events.AddHandler(LastPageCommand, value); }
			remove	{ Events.RemoveHandler(LastPageCommand, value); }
		}

		/// <summary>
		/// Add/Remove handler for the next-page click.
		/// </summary>
		[Category("Action"), Description("Raised when the next-page is clicked")]
		public event CommandEventHandler NextPage
		{
			add		{ Events.AddHandler(NextPageCommand, value); }
			remove	{ Events.RemoveHandler(NextPageCommand, value); }
		}

		/// <summary>
		/// Add/Remove handler for the previous-page click.
		/// </summary>
		[Category("Action"), Description("Raised when the previous-page is clicked")]
		public event CommandEventHandler PreviousPage
		{
			add		{ Events.AddHandler(PreviousPageCommand, value); }
			remove	{ Events.RemoveHandler(PreviousPageCommand, value); }
		}
		#endregion

		#region Overrides
		/// <summary>
		/// Create all controls that will be on the Header. Notice that the creation is more than just instantiating
		/// the controls. It also involves in generating the underlying HTML so that the controls shows up per the need.
		/// </summary>
		protected override void CreateChildControls()
		{
			CreateControls();
			GenerateControlTree();
		}

		/// <summary>
		/// Invoked when an event is raised by a child control.
		/// </summary>
		/// <param name="source">Reference to the child control that raised the event</param>
		/// <param name="e">Argument associated with the event</param>
		/// <returns>True if the event was handle by this method; false otherwise</returns>
		protected override bool OnBubbleEvent(object source, EventArgs e)
		{
			// Events raised only by link buttons are handled. If an event handler is associated with the button, invoke it.

			bool handled = false;
            LinkButton button = source as LinkButton;
            CommandEventArgs args = e as CommandEventArgs;
			if (args != null && button != null)
			{
				CommandEventHandler handler = (CommandEventHandler)Events[args.CommandName];
				if (handler != null)
				{
					handler(this, new CommandEventArgs(button.CommandName, null));
					handled = true;
				}
			}
			return handled;
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Create all controls in the Pager.
		/// The view state for all but the lable which holds the total page count is disabled. The page count needs to hold
		/// state since pages use its contents to compute the index of the last page.
		/// </summary>
		private void CreateControls()
		{
			Controls.Clear();

			currentPage = new Label();
			firstRecord = new Label();
			lastRecord = new Label();
			pageCount = new Label();
			recordCount = new Label();
			recordLabel = new Label();
			recordLabel.Text = RecordLabel;

			leftText = new Label();
			rightText = new Label();
			
			firstPage = new LinkButton();
			firstPage.CommandName = firstPage.Text = FirstPageCommand;
			firstPage.CausesValidation = false;

			lastPage = new LinkButton();
			lastPage.CommandName = lastPage.Text = LastPageCommand;
			lastPage.CausesValidation = false; 

			nextPage = new LinkButton();
			nextPage.CommandName = nextPage.Text = NextPageCommand;
			nextPage.CausesValidation = false; 

			previousPage = new LinkButton();
			previousPage.CommandName = previousPage.Text = PreviousPageCommand;
			previousPage.CausesValidation = false; 
		}

		/// <summary>
		/// Setup the control tree to reflect the actual (and exact) layout of the controls within the Pager.
		/// </summary>
		private void GenerateControlTree()
		{
			Table table = new Table();
			table.CssClass = "Pager";
			Controls.Add(table);

			TableRow row = new TableRow();
			table.Rows.Add(row);

			TableCell cell = new TableCell();
			row.Cells.Add(cell);
			cell.CssClass = "LeftSection";
			cell.Controls.Add(new LiteralControl("<nobr>"));
			
			if (UseOptionalLeftText)
			{
				cell.Controls.Add(leftText);
			}
			else
			{
				cell.Controls.Add(new LiteralControl("&nbsp;"));
				cell.Controls.Add(recordLabel);
				cell.Controls.Add(new LiteralControl("&nbsp;"));
				cell.Controls.Add(firstRecord);
				cell.Controls.Add(new LiteralControl("&nbsp;-&nbsp;"));
				cell.Controls.Add(lastRecord);
				cell.Controls.Add(new LiteralControl("&nbsp;of&nbsp;"));
				cell.Controls.Add(recordCount);
			}

			cell.Controls.Add(new LiteralControl("&nbsp;|&nbsp;"));

			if (UseOptionalRightText)
			{
				cell.Controls.Add(rightText);
			}
			else
			{
				cell.Controls.Add(new LiteralControl("Page&nbsp;"));
				cell.Controls.Add(currentPage);
				cell.Controls.Add(new LiteralControl("&nbsp;of&nbsp;"));
				cell.Controls.Add(pageCount);
			}

			cell.Controls.Add(new LiteralControl("</nobr>"));

			cell = new TableCell();
			row.Cells.Add(cell);
			cell.CssClass = "RightSection";
			cell.Controls.Add(new LiteralControl("<nobr>"));
			cell.Controls.Add(firstPage);
			cell.Controls.Add(new LiteralControl("&nbsp;|&nbsp;"));
			cell.Controls.Add(previousPage);
			cell.Controls.Add(new LiteralControl("&nbsp;|&nbsp;"));
			cell.Controls.Add(nextPage);
			cell.Controls.Add(new LiteralControl("&nbsp;|&nbsp;"));
			cell.Controls.Add(lastPage);
			cell.Controls.Add(new LiteralControl("</nobr>"));
		}

		/// <summary>
		/// Get the integer value of a label's text.
		/// </summary>
		/// <param name="label">Label to retrieve value from</param>
		/// <returns>Integer value (>= 0) on success, -1 on failure</returns>
		private static int GetIntegerSafely(Label label)
		{
			int result = -1;
			try
			{
                if (label.Text != "")
				    result = Convert.ToInt32(label.Text);
			}
			catch{}

			return result;
		}
		#endregion

		#region Internal Methods
		/// <summary>
		/// This method is ONLY invoked by the associated designer to show the control contents at design time.
		/// </summary>
		internal void Design()
		{
			EnsureChildControls();
		}
		#endregion
	}
	#endregion

	#region Class PagerDesigner
	/// <summary>
	/// Designer associated with the Pager control.
	/// </summary>
	/// <remarks>
	/// This class cannot be used directly.
	/// </remarks>
	public class PagerDesigner : ControlDesigner
	{
		#region Overrides
		/// <summary>
		/// Force the Pager control to create its child controls for display at design time.
		/// </summary>
		/// <returns>HTML representation of the Pager control</returns>
		public override string GetDesignTimeHtml()
		{
			((Pager)Component).Design();
			return base.GetDesignTimeHtml();
		}
		#endregion
	}
	#endregion
}

// --- EOF ---
